import copy
from functools import wraps

from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.contrib.auth.models import User, Permission
# from django.contrib.sessions.backends.db import SessionStore
from django.contrib.sessions.models import Session
from django.db import transaction
from django.http import JsonResponse
# from requests import session
from rest_framework import status
import json
from backend.cews.models import Log, StockItemGroup, RepairOrderPart, RepairOrder
from backend.users.models import GroupSetting
import backend.cews.services.LogService as LogService
import backend.cews.services.RepairService as RepairService
from backend.cews.services.LogService import check_description_and_update

from django.utils import timezone

User = get_user_model()


def check_permission(permission_list=[]):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            request = args[1]
            user: User
            user = request.user
            if (user.has_perms(permission_list)):
                return func(*args, **kwargs)
            else:
                return JsonResponse(
                    data={
                        "message": "Insufficient permission"
                    },

                    status=status.HTTP_403_FORBIDDEN
                )
        return wrapper
    return decorator


def data_required(keys=[]):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            request = args[1]
            data = copy.copy(request.data)
            for k in keys:
                if k not in data:
                    return JsonResponse(
                        data={
                            'message': f"{k} is missing",
                        },

                        status=status.HTTP_400_BAD_REQUEST
                    )
            return func(*args, **kwargs)
        return wrapper
    return decorator


def get_required(keys=[]):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            request = args[1]
            data = copy.copy(request.GET)
            for k in keys:
                if k not in data:
                    return JsonResponse(
                        data={
                            'message': f"{k} is missing",
                        },

                        status=status.HTTP_400_BAD_REQUEST
                    )
            return func(*args, **kwargs)
        return wrapper
    return decorator


def required_master():
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            request = args[1]
            data = copy.copy(request.headers)
            Sessions = Session.objects.all()

            for row in Sessions:
                if row.get_decoded().get('master_token') \
                        == data['Master-Token'][6:]:

                    return func(*args, **kwargs)

            return JsonResponse(
                data={
                    'message': "Invalid master token, please login",
                },

                status=status.HTTP_400_BAD_REQUEST
            )
        return wrapper
    return decorator

def request_data(keys, request_attr="data", types={},optional=[]):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            request = args[1]
            data = getattr(request, request_attr)
            for key in keys:
                if key not in data and key not in optional:
                    return JsonResponse(
                        status=400,
                        data={
                            "message": f"{key} is required.",
                            "code": 0
                        }
                    )
                expected_type = types.get(key)
                if key in data \
                    and expected_type is not None \
                        and not isinstance(data[key], expected_type):

                    return JsonResponse(
                        status=400,
                        data={
                            "message": f"{key} must be of type {expected_type}.",
                            "code": 0
                        }
                    )

                if key in data:
                    kwargs[key] = data[key]

            return func(*args,  **kwargs)
        setattr(wrapper, 'request_data_keys', keys)
        setattr(wrapper, 'request_data_types', types)
        setattr(wrapper, 'request_data_optional', optional)
        return wrapper
    return decorator


def required_superuser():
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            request = args[1]
            user: User
            user = request.user
            if (user.is_superuser):
                return func(*args, **kwargs)
            else:
                return JsonResponse(
                    {
                        "message": "You are not superuser"
                    },

                    status=status.HTTP_403_FORBIDDEN
                )
        return wrapper
    return decorator


def error_response(*error_types):
    if not error_types:
        error_types = (ValueError,)

    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except error_types as ex:
                return JsonResponse(data={
                    'code': 0,
                    'message': str(ex),
                }, status=400)
        wrapper.__name__ = func.__name__
        return wrapper
    return decorator


# def error_response():
#     def decorator(func):
#         @wraps(func)
#         def wrapper(*args, **kwargs):
#             return func(*args,  **kwargs)
#         return wrapper
#     return decorator


def request_data_view(keys, request_attr="data", types={}, optional=[]):
    '''
        deocrator for function based view
    '''
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            request = args[0]
            data = getattr(request, request_attr)
            for key in keys:
                if key not in data and key not in optional:
                    return JsonResponse(
                        status=400,
                        data={
                            "message": f"{key} is required.",
                            "code": 0
                        }
                    )
                expected_type = types.get(key)
                if expected_type is not None \
                        and not isinstance(data[key], expected_type):

                    return JsonResponse(
                        status=400,
                        data={
                            "message": f"{key} must be of type {expected_type}.",
                            "code": 0
                        }
                    )
                kwargs[key] = data[key]

            return func(*args,  **kwargs)
        setattr(wrapper, 'request_data_keys', keys)
        setattr(wrapper, 'request_data_types', types)
        setattr(wrapper, 'request_data_optional', optional)
        return wrapper
    return decorator


def activities_logging(log_type,category_type,action_type, action, description):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            request = args[1]
            print(description)
            with transaction.atomic():
                pk = None
                
                if 'pk' in kwargs:
                    print(kwargs['pk'])
                    pk = kwargs['pk']

                data = LogService.check_description_and_update(log_type ,description, request.data, pk)
                
                if data:
                    new_log = Log(
                        username=request.user.username,
                        log_type=log_type,
                        category_type=category_type,
                        action_type=action_type,
                        action=action,
                        payload= request.data,
                        description=data['new_description'],
                        repair_order=data["repair_order_id"],
                        parent_stock_code=data["parent_stock_code"],
                        parent_part=data["parent_serial_no"],
                        part=data["serial_no"],
                        stock_code=data["stock_code"]
                    )

                    new_log.save()

                    if data["repair_order_id"]:
                        RepairService.update_repair_order_last_action_date(data["repair_order_id"])

            return func(*args, **kwargs)
        return wrapper
    return decorator

def check_group_permission(permission=''):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            request = args[1]
            user: User
            user = request.user
            print('cews.'+ permission)
            if (user.is_superuser):
                return func(*args, **kwargs)
            if not (user.has_perm('cews.'+ permission)):
                raise ValueError(f"Not enough permission")
            return func(*args, **kwargs)
        return wrapper
    return decorator

def check_stock_item_group():
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            request = args[1]
            user: User
            user = request.user

            if (user.is_superuser):
                return func(*args, **kwargs)

            repair_id = kwargs['pk']

            r = RepairOrder.objects.get(pk=repair_id)
            print(f"{r}=")

            # check_have_stock_group_permission(user, rops)
                
            now_date = timezone.now().date()
            # now_date = now.strftime("%Y-%m-%d")

            messages = []


            stock_codes = []
        
            for rop in RepairOrderPart.objects.filter(repair_order=r):
                if rop.stock_code not in stock_codes:
                    stock_codes.append(rop.stock_code)
            
            # print(stock_codes)


            sigs = StockItemGroup.objects.filter(stock_code__in=stock_codes)

            for group in user.groups.all():
                sigs = StockItemGroup.objects.filter(stock_code__in=stock_codes, group=group)
                # print(sigs)
                # print(sigs.count())
                # print(len(stock_codes))
                if sigs.count() == len(stock_codes):
                    for sig in sigs:
                        if sig.group.permissions.filter(codename='can_do_maintenance_by_stock_code').exists():
                            # and user.groups.filter(name=sig.group.name).exists():
                            # gs = GroupSetting.objects.filter(group=sig.group, expiry_date__gte=now, enable=True)
                            gss = GroupSetting.objects.filter(group=sig.group,users=user, enable=True)
                            for gs in gss:
                                if gs.expiry_date >= now_date:
                                    return func(*args, **kwargs)
                                    
                                else:
                                    messages.append(f"Insufficient permission. Group: {sig.group.name} in stock item group is expiry at {gs.expiry_date}")

                        else:
                            messages.append(f"Insufficient permission. User not in stock item group")

            messages.append(f"Insufficient permission. No stock item group include all stock code")

            if len(messages) > 0:
                return JsonResponse(
                    data={
                        "messages": 'Insufficient permission',
                        "notice": messages
                    },
                    status=status.HTTP_403_FORBIDDEN
                )
            # print('can have permission')
            # return func(*args, **kwargs)
        return wrapper
    return decorator

            # for stock_code in stock_codes:
            #     print(sigs)
            #     if sigs.exists():
            #         for sig in sigs:
            #             if sig.group.permissions.filter(codename='can_do_maintenance_by_stock_code').exists():
            #                 gss = GroupSetting.objects.filter(group=sig.group, expiry_date__gte=now, enable=True)
            #                 if gss.exists():
            #                     for gs in gss:
            #                         if gs.filter(user=request.user)
            #                     return func(*args, **kwargs)
            #                 else:
            #                     messages.append(f"Insufficient permission group:{sig.group} is expiry")
            #                     continue
            #             else:
            #                 messages.append(f"Insufficient permission in stock item group")
            #                 continue
            #     else:
            #         messages.append(f"Insufficient permission. Stock Code: {stock_code} not in any stock item group")
            #         continue
 
            # for group in user.groups.all():
            #     if group.permissions.filter(codename='can_do_maintenance_by_stock_code').exists():
            #         if GroupSetting.objects.filter(group=group, expiry_date__gte=now, enable=True).exists():
            #             stock_item_group_dict = {
            #                 sig.stock_code : sig
            #                 for sig in StockItemGroup.objects.filter(group=group)
            #             }

            #             for rop in RepairOrderPart.objects.filter(repair_order=r):
            #                 print(group)
            #                 print(rop.stock_code)
            #                 if rop.stock_code in stock_item_group_dict:
            #                     continue  
            #                 else:
            #                     messages.append(f"Insufficient permission in Stock Code: {rop.stock_code}")
                     
            #         else:
            #             messages.append(f"Insufficient permission, group: ''{group}'' is expiry")
            #     else:
            #         messages.append("Insufficient permission to do maintenance")
                    

